#include "CD3D9Mesh.h"

#include "D3D9\CD3D9RenderSystem.h"
#include "Render\CMaterial.h"
#include "Render\CTexture.h"

namespace Nervus {
	namespace Render {
		//----------------------------------------------------------------------------------
		D3D9Mesh::D3D9Mesh(void* renderer, vector<MeshVertex> vrts)
			: mMeshType(NVMT_POLYGONSTRIP), mVertexBuffer(NULL), mIndexBuffer(NULL), mTexture(NULL)
		{
			mRenderSystem = static_cast<D3D9RenderSystem*>(renderer);
			mVertices = vrts;

		}
		//----------------------------------------------------------------------------------
		D3D9Mesh::~D3D9Mesh(){
			mVertexBuffer->Release();
			mIndexBuffer->Release();
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------
		bool D3D9Mesh::loadMesh(){
			Texture* texture = mMaterial->mTextures[0];
			if (texture != NULL) {
				if (!texture->loadImageFromFile()){
					return false;
				}


				if (FAILED(mRenderSystem->mVideoCard->CreateTexture(texture->mImageWidth,
					texture->mImageHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &mTexture, NULL)))
				{
					return false;
				}


				//DirectX reads texture data from the end
				//So image data needs to be reversed
				u32 imageSize = texture->mImageHeight * texture->mImageWidth * 4;
				u8* revData = new u8[imageSize];

				for (i32 c = texture->mImageHeight - 1; c >= 0; c--){	//Processes rows
					for (u32 r = 0; r < texture->mImageWidth; r++){		//Processes one row
						for (u32 b = 0; b < 4; b++){					//Processes pixel (4-byte chunk)
							revData[c*texture->mImageWidth * 4 + r * 4 + b] =
								texture->mImageData[(texture->mImageHeight - c - 1)*texture->mImageWidth * 4 + r * 4 + (3 - b)];
						}
					}
				}


				D3DLOCKED_RECT rect;
				mTexture->LockRect(0, &rect, NULL, D3DLOCK_DISCARD);
				u8* pbyDst = (u8*)(rect.pBits);
				memcpy(pbyDst, revData, texture->mImageHeight * texture->mImageWidth * 4);
				mTexture->UnlockRect(0);
				delete[] revData;

			}


			mRenderSystem->mVideoCard->CreateVertexBuffer(mVertices.size() * sizeof(MeshVertex), 0, VERTEX_FORMAT_3D, 
				D3DPOOL_MANAGED, &mVertexBuffer, NULL);

			void* pVertices;
			mVertexBuffer->Lock(0, 0, (void**)&pVertices, 0);
			memcpy(pVertices, &(mVertices[0]), mVertices.size() * sizeof(MeshVertex));
			mVertexBuffer->Unlock();

			mRenderSystem->mVideoCard->CreateIndexBuffer(mVertices.size() * 2, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, 
				D3DPOOL_MANAGED, &mIndexBuffer, NULL);

			WORD* indices = NULL;
			mIndexBuffer->Lock(0, 0, (void**)&indices, 0);
			for (u32 i = 0; i < mVertices.size(); i++){
				indices[i] = i;
			}
			mIndexBuffer->Unlock();

			return true;
		}
		//----------------------------------------------------------------------------------
		void D3D9Mesh::renderMesh(){
			mRenderSystem->mVideoCard->SetFVF(VERTEX_FORMAT_3D);

			if (mMeshType == NVMT_POINTLIST){
				mRenderSystem->mVideoCard->SetTexture(0, NULL);
			}
			else if (mTexture != NULL){
				mRenderSystem->mVideoCard->SetTexture(0, mTexture);
			}
			else {
				mRenderSystem->mVideoCard->SetTexture(0, mRenderSystem->mDefaultTexture);
			}




			mRenderSystem->mVideoCard->SetStreamSource(0, mVertexBuffer, 0, sizeof(MeshVertex));
			mRenderSystem->mVideoCard->SetIndices(mIndexBuffer);

			switch (mMeshType){
			case NVMT_POLYGONLIST:
				mRenderSystem->mVideoCard->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mVertices.size(),
					0, mVertices.size() / 3);
				break;
			case NVMT_POLYGONSTRIP:
				mRenderSystem->mVideoCard->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, mVertices.size(),
					0, mVertices.size() - 2);
				break;
			case NVMT_POLYGONFAN:
				mRenderSystem->mVideoCard->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, 0, mVertices.size(),
					0, mVertices.size() - 2);
				break;
			case NVMT_LINELIST:
				mRenderSystem->mVideoCard->DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, mVertices.size(),
					0, mVertices.size() / 2);
				break;
			case NVMT_LINESTRIP:
				mRenderSystem->mVideoCard->DrawIndexedPrimitive(D3DPT_LINESTRIP, 0, 0, mVertices.size(),
					0, mVertices.size() - 1);
				break;
			case NVMT_POINTLIST:
				mRenderSystem->mVideoCard->DrawPrimitive(D3DPT_POINTLIST, 0, mVertices.size());
				break;
			}
		}
		//----------------------------------------------------------------------------------
	}
}